package groovy.lang.webobjects;

import groovy.lang.GroovyObject;

import java.lang.reflect.Constructor;
import java.util.Enumeration;

import com.webobjects.appserver.WOApplication;
import com.webobjects.appserver.WOComponent;
import com.webobjects.appserver.WOContext;
import com.webobjects.appserver.WODirectAction;
import com.webobjects.appserver.WORequest;
import com.webobjects.appserver.WORequestHandler;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSKeyValueCoding;
import com.webobjects.foundation.NSNotification;
import com.webobjects.foundation.NSNotificationCenter;
import com.webobjects.foundation.NSSelector;
import com.webobjects.foundation._NSUtilities;


public class WOGroovyClassHandler {
	private static boolean initialized = false;
	private static WOGroovyClassHandler handler = null;

	public static WOGroovyClassHandler getClassHandler() {
		if (handler == null)
			handler = new WOGroovyClassHandler();
		return handler;
	}

	public void initialize() {
		if (initialized)
			return;

		initialized = true;

		if (WOApplication.application() != null &&
				WOApplication.application().isCachingEnabled()) {
			System.out.println("Running in deployment mode. WOGroovy rapid turnaround is disabled");
			return;
		}

		if (!groovy.lang.webobjects.WOGroovy.javaAgentRunning) {
			System.out.println("WOGroovy Rapid turnaround mode is disabled because the JavaAgent is not running " +
					"\n    To use Rapid Turnaround you must add the following to your " +
					"Java VM arguments:\n        -javaagent:<pathtojar>/gluonj.jar=JavaAgent");
			System.out.println("    gluonj.jar is part of the WOGroovy framework and requires Java 1.5 or newer");
			return;
		}

		if(!Boolean.parseBoolean(System.getProperty("wogroovy.rapidturnaround", "false"))) {
			System.out.println("Rapid-turnaround mode is disabled, set 'wogroovy.rapidturnaround=true'" +
			" in your WebObjects.properties to enable it.");
			return;
		}

		System.out.println("WOGroovy rapid turnaround mode enabled");
		NSNotificationCenter.defaultCenter().addObserver(this,
				new NSSelector("updateLoadedClasses", new Class[] { NSNotification.class } ),
				WOApplication.ApplicationWillDispatchRequestNotification, null);
	}

	public void updateLoadedClasses(NSNotification n) {
		boolean doReset = true;
		WORequest r = (WORequest)n.object();
		String key = "/" + WOApplication.application().resourceRequestHandlerKey();
		if(r.uri().indexOf(r.adaptorPrefix()) != 0 || r.uri().indexOf(key) >= 0)
			return;

		// If any classes have changed we reset the class loader
		if (WOGroovyClassLoader.reloadRequired())
			WOGroovyClassLoader.resetClassLoader();
		
		NSDictionary<String, Class<?>> classList = WOGroovyClassCache.getClassCache();
		String unknownName = "com.webobjects.foundation._NSUtilities$_NoClassUnderTheSun";
		Class<?> unknownClass = _NSUtilities._classWithFullySpecifiedName(unknownName);
		ClassLoader loader = WOGroovyClassLoader.getClassLoader();
		for (Enumeration<String> en = classList.keyEnumerator(); en.hasMoreElements();) {
			String className = en.nextElement();

			if (className.equals(unknownName)) continue;
			String classRealName = ((Class<?>)classList.valueForKey(className)).getName();
			boolean doUpdate = false;

			Class<?> oldClass_ = WOGroovyClassCache.classForName(className);

			if (oldClass_ == null || oldClass_.equals(unknownClass)) {
				WOGroovyClassCache.removeClassForName(className);
				doReset = true;
				continue;
			}

			Class<?>[] interfaces = oldClass_.getInterfaces();

			for (int i = 0; i < interfaces.length; i++) {
				if (interfaces[i].equals(GroovyObject.class))
					doUpdate = true;
			}
			if (!doUpdate) continue;
			
			Class<?> class_;
			try {				
				class_ = Class.forName(oldClass_.getName(), true, loader);
			} catch (Exception e) {
				System.out.println("Error reloading the class " + className + " : " + e.getMessage());
				continue;
			}

			if (class_ == null)
				continue;

			if (!class_.equals(oldClass_)) {
//				System.out.println("Resetting class " + oldClass_.getName());
				doReset = true;
				WOGroovyClassCache.setClassForName(class_, className);

				if(oldClass_ != null && !className.equals(classRealName))
					WOGroovyClassCache.setClassForName(class_, classRealName);
			}

			if(WODirectAction.class.isAssignableFrom(class_)) {
				WOApplication app = WOApplication.application();
				WORequestHandler currentDAHandler = app.requestHandlerForKey(app.directActionRequestHandlerKey());
				WORequestHandler handler = null;
				try {
					Class<?>[] paramArray = new Class[] { String.class, String.class, Boolean.TYPE };
					Constructor<? extends WORequestHandler> constructor = currentDAHandler.getClass().getConstructor(paramArray);
					handler = constructor.newInstance(new Object[] { className, "default", Boolean.FALSE } );
				} catch(Exception ex) {
				}
				boolean directActionIsDefault = currentDAHandler == app.defaultRequestHandler();
				app.registerRequestHandler(handler, app.directActionRequestHandlerKey());
				if(directActionIsDefault)
					app.setDefaultRequestHandler(handler);
			}
			if(WOComponent.class.isAssignableFrom(class_)) {
				WOContext context = newContext();
				WOApplication.application().pageWithName(className, context)
					._componentDefinition().componentInstanceInContext(context);
			}
		}

		if(doReset) {
			WOApplication.application()._removeComponentDefinitionCacheContents();
			NSKeyValueCoding.DefaultImplementation._flushCaches();
			NSKeyValueCoding._ReflectionKeyBindingCreation._flushCaches();
			NSKeyValueCoding.ValueAccessor._flushCaches();
		}
	}
	public static WOContext newContext(){
        WOApplication app = WOApplication.application();
        return app.createContextForRequest(app.createRequest("GET", app.cgiAdaptorURL() + "/"
        		+ app.name(), "HTTP/1.1", null, null, null));
    }
}
